package com.migu.rbac.config;

import com.migu.rbac.model.SysBackApi;
import com.migu.rbac.model.SysRole;
import com.migu.rbac.service.SysMenuService;
import com.migu.rbac.repository.SysBackApiRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description TODO :
 * @Author: zj
 * @Date: 10:37 2019/3/27
 * @Version 1.0
 **/
@Component
public class DaoSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    @Autowired
    private SysBackApiRepository apiRepository;

    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        FilterInvocation fi = (FilterInvocation) o;
        List<SysRole> neededRoles = this.getRequestNeededRoles(fi.getRequest().getMethod(), fi.getRequestUrl());
        if (neededRoles != null) {
            return SecurityConfig.createList(neededRoles.stream().map(role -> role.getRolename()
            ).collect(Collectors.toList()).toArray(new String[]{}));
        }
        return SecurityConfig.createList("ROLE_LOGIN");
    }

    /**
     * 获取到干净的RequestUrl（去掉参数）,然后看是否有对应的backendAPI，
     * 如果没有，则有可能该API有path参数，我们可以去掉最后的path，去库里模糊匹配，直到找到。
     *
     * @param method
     * @param path
     * @return
     */
    private List<SysRole> getRequestNeededRoles(String method, String path) {
/*        Object o = redisUtil.get(RedisCahceConstant.URLPATHMETHOD_FIRST + path + method);
        if (o != null) {
            return JSONArray.parseArray(o.toString(), SysRole.class);
        }*/
        String rawPath = path;
        if (path.indexOf("?") > -1) {
            path = path.substring(0, path.indexOf("?"));
        }
        Map<String, String> map = new HashMap<>();
        map.put("path", path);
        map.put("method", method);

        SysBackApi api = apiRepository.findByPathAndMethod(map);
        if (api == null) {
            // try fetch by remove last path
            api = loadFromSimilarApi(method, path, rawPath);
        }
        if (api != null && api.getMenus().size() > 0) {
//            List<SysRole> collect = api.getMenus().stream()
//                    .flatMap(munu -> sysMenuService.findOneWithRolesById(munu.getId()).getRoles().stream())
//                    .collect(Collectors.toList());
            return api.getMenus().stream()
                    .flatMap(menu -> menu.getRoles().stream()).collect(Collectors.toList());
        }
        return null;
    }

    private SysBackApi loadFromSimilarApi(String method, String path, String rawPath) {
        if (path.lastIndexOf("/") > -1) {
            path = path.substring(0, path.lastIndexOf("/"));
            Map<String, String> map = new HashMap<>();
            map.put("path", path);
            map.put("method", method);
            List<SysBackApi> apis = apiRepository.findByPathStartsWithAndMethod(map);
            // 如果为空，再去掉一层path
            while (apis == null) {
                if (path.lastIndexOf("/") > -1) {
                    path = path.substring(0, path.lastIndexOf("/"));
                    apis = apiRepository.findByPathStartsWithAndMethod(map);
                } else {
                    break;
                }
            }
            PathMatcher antPathMatcher = new AntPathMatcher();
            if (apis != null) {
                for (SysBackApi backendApi : apis) {
                    if (antPathMatcher.match(backendApi.getPath(), rawPath)) {
                        return backendApi;
                    }
                }
            }
        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return FilterInvocation.class.isAssignableFrom(aClass);
    }
}
